#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>
#include <unistd.h>
#include <cstdio>

using namespace std;

/*
1.Linux内核没有真正意义的线程 而是用PCB来模拟线程
2.由于进程内部有一个或多个执行流 因此将一个PCB看作一个轻量级进程即线程 将整个PCB 地址空间 页表 物理内存的代码和数据看作进程
3.线程是CPU调度的基本单位 进程是承担系统资源分配的基本单位 进程整体用于申请资源 线程用于向进程申请资源

Linux线程设计的优点:减少代码冗余和维护成本 缺点: 没有创建线程的接口 只有创建轻量级进程的接口 需要引入线程库

线程间的切换比进程间的切换代价小很多 主要体现在cache上 线程间切换时 cache仍然能保持一定的命中率
而进程间切换时 cache中的数据会立马失效 需要重新缓存数据

线程私有的部分 PCB内核资源 上下文环境 栈结构
*/


//单进程单线程
//全局变量和方法能被所有线程访问
// int g_val = 0;

// string func()
// {
//     return "我是一个独立的方法";
// }

// void* thread_routine(void* args)//从线程
// {
//     char* name = (char*)args;
//     while (true)
//     {
//         printf("我是从线程:%s func:%s g_val:%d\n", name, func().c_str(), g_val++);
//         sleep(1);
//     }
// }

// int main()
// {
//     pthread_t tid;
//     //创建一个线程并执行回调 同时将id返回
//     pthread_create(&tid, nullptr, thread_routine, (void*)"thread1");
//     //主线程
//     while (true)
//     {
//         //tid解密
//         char tidbuffer[64];
//         snprintf(tidbuffer, sizeof(tidbuffer), "0x%x", tid);
//         printf("我是主线程 我创建的线程id:%s func:%s g_val:%d\n",tidbuffer, func().c_str(), g_val++);
//         sleep(1);   
//     }
//     return 0;
// }

//单进程多线程 创建线程->线程退出->线程等待
/*
线程一般通过返回值退出 返回值暂时存放在线程库中 主线程join该线程时 将二级指针传入获取线程库存放的返回值 即可获取线程的退出信息
*/
#define NUM 10 //线程数

struct threadData
{
    pthread_t tid;
    char namebuffer[64];
    int number;
};

struct threadReturn
{
    int exit_code;
    int exit_result;
};

//1
// void* thread_routine(void* args)
// {
//     char* name = (char*)args;
//     int cnt = 10;
//     while (cnt)
//     {
//         printf("我是从线程 name:%s cnt:%d\n", name, cnt--);
//         sleep(1);
//     }
// }

//2
void* thread_routine(void* args)
{
    sleep(1);
    threadData* td = (threadData*)args;
    int cnt = 10;
    while (cnt)
    {
        printf("我是从线程 name:%s cnt:%d\n", td->namebuffer, cnt--);
        sleep(1);
    }
    /*
    线程退出的方式 1.正常退出 2.返回值退出 3.pthread_exit退出 4.主线程pthread_cancel取消线程
    */
    return (void*)106;
    //pthread_exit((void*)106);

    // threadReturn* exit_info = new threadReturn;
    // exit_info->exit_code = td->number;
    // exit_info->exit_result = 106;
    // return (void*)exit_info;
    //pthread_exit((void*)exit_info);
}

int main()
{
    //1 用该方法创建多线程打印线程名 在从线程执行流还未开始时 namebuffer已经进行多次刷新 导致最终打印的name相同
    // pthread_t tid;
    // for (int i = 0; i < NUM; i++)
    // {
    //     char namebuffer[64];
    //     snprintf(namebuffer, sizeof(namebuffer), "thread%d", i + 1);
    //     pthread_create(&tid, nullptr, thread_routine, (void*)namebuffer);
    // }
    // while (true)
    // {
    //     cout << "我是主线程" << endl;
    //     sleep(1);
    // }

    //2 通过传入对象指针打印线程名 可以保证每次传入的地址空间不同 从而打印不同的线程名
    vector<threadData*> threads;
    for (int i = 0; i < NUM; i++)
    {
        threadData* td = new threadData;
        td->number = i + 1;
        snprintf(td->namebuffer, sizeof(td->namebuffer), "thread%d", i + 1);
        pthread_create(&(td->tid), nullptr, thread_routine, (void*)td);
        threads.push_back(td);
    }
    for (auto item : threads)
    {
        printf("thread%d create success\n", item->number);
    }
    //pthread_cancel取消线程
    sleep(3);//成功创建线程后让线程先跑一会再取消
    for (int i = 0; i < NUM / 2; i++)
    {
        pthread_cancel(threads[i]->tid);
        printf("thread%d cancel success\n", threads[i]->number);
    }
    //线程等待 获取从线程退出信息 回收从线程资源
    for (auto item : threads)
    {
        void* ret = nullptr;
        pthread_join(item->tid, &ret);//线程退出信息通过二级指针返回给ret
        printf("thread%d join success exit_code:%d\n", item->number, (long long)ret);
        // printf("thread%d join success exit_code:%d exit_result:%d\n", item->number, 
        //                                                              ((threadReturn*)ret)->exit_code,
        //                                                              ((threadReturn*)ret)->exit_result);
        //统一释放堆区空间
        delete item;
        //delete (threadReturn*)ret;
    }
    sleep(5);
    cout << "main thread quit" << endl;
    return 0;
}
